using System.Windows.Controls;
using System.Windows;
using System;
using System.Reflection;

using PSE.Framework.UI.Controls.SmartClient.Validation;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using PSE.Framework.UI.Controls.SmartClient;
using System.Drawing;
using PSE.Framework.Serialization;


namespace PSE.Framework.UI.Controls.SmartClient
{
    [TemplatePart(Name = "PART_DropDown", Type = typeof(ToggleButton))]
    [TemplatePart(Name = "PART_RepeatLeft", Type = typeof(RepeatButton))]
    [TemplatePart(Name = "PART_RepeatRight", Type = typeof(RepeatButton))]
    [TemplatePart(Name = "PART_NewTabButton", Type = typeof(ButtonBase))]
    [TemplatePart(Name = "PART_ScrollViewer", Type = typeof(ScrollViewer))]
    public class TabControlBrowser : TabControl
    {
        #region Attributes

        // public Events
        public event RoutedEventHandler TabItemBrowserAdded;
        public event EventHandler<CancelEventArgs> TabItemBrowserClosing;
        public event RoutedEventHandler TabItemBrowserClosed;

        // TemplatePart controls
        private System.Windows.Controls.Primitives.ToggleButton _toggleButton;

        #endregion

        #region Constructor

        public TabControlBrowser() : base()
        {
            CommandBindings.Add(new CommandBinding(TabControlCommands.CreateTabItem, createTabItemClick));
            CommandBindings.Add(new CommandBinding(TabControlCommands.RepeatLeft, repeatLeftClick));
            CommandBindings.Add(new CommandBinding(TabControlCommands.RepeatRight, repeatRightClick));
        }

        #endregion

        #region Dependancy Properties

        /// <summary>
        /// Allow the User to Add New TabItems
        /// </summary>
        public bool AllowAddNew
        {
            get { return (bool)GetValue(AllowAddNewProperty); }
            set { SetValue(AllowAddNewProperty, value); }
        }
        public static readonly DependencyProperty AllowAddNewProperty = DependencyProperty.Register("AllowAddNew", typeof(bool), typeof(TabControlBrowser), new UIPropertyMetadata(true));

        /// <summary>
        /// Allow the User to Delete TabItems
        /// </summary>
        public bool AllowDelete
        {
            get { return (bool)GetValue(AllowDeleteProperty); }
            set { SetValue(AllowDeleteProperty, value); }
        }
        public static readonly DependencyProperty AllowDeleteProperty = DependencyProperty.Register("AllowDelete", typeof(bool), typeof(TabControlBrowser), new UIPropertyMetadata(true));

        /// <summary>
        /// Set new TabItem as the current selection
        /// </summary>
        public bool SelectNewTabOnCreate
        {
            get { return (bool)GetValue(SelectNewTabOnCreateProperty); }
            set { SetValue(SelectNewTabOnCreateProperty, value); }
        }
        public static readonly DependencyProperty SelectNewTabOnCreateProperty = DependencyProperty.Register("SelectNewTabOnCreate", typeof(bool), typeof(TabControlBrowser), new UIPropertyMetadata(true));

        /// <summary>
        /// Determines where new TabItems are added to the TabControlBrowser
        /// </summary>
        /// <remarks>
        ///     Set to true (default) to add all new Tabs to the end of the TabControlBrowser
        ///     Set to False to insert new tabs after the current selection
        /// </remarks>
        public bool AddNewTabToEnd
        {
            get { return (bool)GetValue(AddNewTabToEndProperty); }
            set { SetValue(AddNewTabToEndProperty, value); }
        }
        public static readonly DependencyProperty AddNewTabToEndProperty = DependencyProperty.Register("AddNewTabToEnd", typeof(bool), typeof(TabControlBrowser), new UIPropertyMetadata(true));

        /// <summary>
        /// defines the Minimum width of a TabItem
        /// </summary>
        [DefaultValue(80)]
        [Category("Layout")]
        public double TabItemMinWidth
        {
            get { return (double)GetValue(TabItemMinWidthProperty); }
            set { SetValue(TabItemMinWidthProperty, value); }
        }
        public static readonly DependencyProperty TabItemMinWidthProperty = DependencyProperty.Register("TabItemMinWidth", typeof(double), typeof(TabControlBrowser),
            new FrameworkPropertyMetadata(80.0, new PropertyChangedCallback(OnMinMaxChanged), new CoerceValueCallback(CoerceMinWidth)));

        private static object CoerceMinWidth(DependencyObject d, object value)
        {
            TabControlBrowser tc = (TabControlBrowser)d;
            double newValue = (double)value;

            if (newValue > tc.TabItemMaxWidth)
                return tc.TabItemMaxWidth;

            return (newValue > 0 ? newValue : 0);
        }

        /// <summary>
        /// defines the Minimum height of a TabItem
        /// </summary>
        [DefaultValue(20)]
        [Category("Layout")]
        public double TabItemMinHeight
        {
            get { return (double)GetValue(TabItemMinHeightProperty); }
            set { SetValue(TabItemMinHeightProperty, value); }
        }
        public static readonly DependencyProperty TabItemMinHeightProperty = DependencyProperty.Register("TabItemMinHeight", typeof(double), typeof(TabControlBrowser),
            new FrameworkPropertyMetadata(30.0, new PropertyChangedCallback(OnMinMaxChanged), new CoerceValueCallback(CoerceMinHeight)));

        private static object CoerceMinHeight(DependencyObject d, object value)
        {
            TabControlBrowser tc = (TabControlBrowser)d;
            double newValue = (double)value;

            if (newValue > tc.TabItemMaxHeight)
                return tc.TabItemMaxHeight;

            return (newValue > 0 ? newValue : 0);
        }

        /// <summary>
        /// defines the Maximum width of a TabItem
        /// </summary>
        [DefaultValue(double.PositiveInfinity)]
        [Category("Layout")]
        public double TabItemMaxWidth
        {
            get { return (double)GetValue(TabItemMaxWidthProperty); }
            set { SetValue(TabItemMaxWidthProperty, value); }
        }
        public static readonly DependencyProperty TabItemMaxWidthProperty = DependencyProperty.Register("TabItemMaxWidth", typeof(double), typeof(TabControlBrowser),
            new FrameworkPropertyMetadata(double.PositiveInfinity, new PropertyChangedCallback(OnMinMaxChanged), new CoerceValueCallback(CoerceMaxWidth)));

        private static object CoerceMaxWidth(DependencyObject d, object value)
        {
            TabControlBrowser tc = (TabControlBrowser)d;
            double newValue = (double)value;

            if (newValue < tc.TabItemMinWidth)
                return tc.TabItemMinWidth;

            return newValue;
        }

        /// <summary>
        /// defines the Maximum width of a TabItem
        /// </summary>
        [DefaultValue(double.PositiveInfinity)]
        [Category("Layout")]
        public double TabItemMaxHeight
        {
            get { return (double)GetValue(TabItemMaxHeightProperty); }
            set { SetValue(TabItemMaxHeightProperty, value); }
        }
        public static readonly DependencyProperty TabItemMaxHeightProperty = DependencyProperty.Register("TabItemMaxHeight", typeof(double), typeof(TabControlBrowser),
            new FrameworkPropertyMetadata(double.PositiveInfinity, new PropertyChangedCallback(OnMinMaxChanged), new CoerceValueCallback(CoerceMaxHeight)));

        private static object CoerceMaxHeight(DependencyObject d, object value)
        {
            TabControlBrowser tc = (TabControlBrowser)d;
            double newValue = (double)value;

            if (newValue < tc.TabItemMinHeight)
                return tc.TabItemMinHeight;

            return newValue;
        }

        ///// <summary>
        ///// CanScrollLeftOrUp Dependancy Property
        ///// </summary>
        //[Browsable(false)]
        //internal bool CanScrollLeftOrUp
        //{
        //    get { return (bool)GetValue(CanScrollLeftOrUpProperty); }
        //    set { SetValue(CanScrollLeftOrUpProperty, value); }
        //}

        //private static readonly DependencyProperty CanScrollLeftOrUpProperty = DependencyProperty.Register("CanScrollLeftOrUp", typeof(bool), typeof(VirtualizingTabPanel), new UIPropertyMetadata(false));

        ///// <summary>
        ///// CanScrollRightOrDown Dependancy Property
        ///// </summary>
        //[Browsable(false)]
        //internal bool CanScrollRightOrDown
        //{
        //    get { return (bool)GetValue(CanScrollRightOrDownProperty); }
        //    set { SetValue(CanScrollRightOrDownProperty, value); }
        //}

        //private static readonly DependencyProperty CanScrollRightOrDownProperty = DependencyProperty.Register("CanScrollRightOrDown", typeof(bool), typeof(VirtualizingTabPanel), new UIPropertyMetadata(false));

        #endregion

        #region Methods

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new TabItemBrowser();
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is TabItemBrowser);
        }

        /// <summary>
        /// OnMinMaxChanged callback responds to any of the Min/Max dependancy properties changing
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnMinMaxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TabControlBrowser tc = (TabControlBrowser)d;
            if (tc.Template == null) return;

            VirtualizingTabPanel tp = (VirtualizingTabPanel)ControlHelper.FindChild(tc, typeof(VirtualizingTabPanel));
            if (tp != null)
                tp.InvalidateMeasure();
        }

        /// <summary>
        /// OnApplyTemplate override
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _toggleButton = this.Template.FindName("PART_DropDown", this) as System.Windows.Controls.Primitives.ToggleButton;
            if (_toggleButton != null)
            {
                // create a context menu for the togglebutton
                ContextMenu cm = new ContextMenu();
                cm.PlacementTarget = _toggleButton;
                cm.Placement = PlacementMode.Bottom;

                // create a binding between the togglebutton's IsChecked Property
                // and the Context Menu's IsOpen Property
                Binding b = new Binding();
                b.Source = _toggleButton;
                b.Mode = BindingMode.TwoWay;
                b.Path = new PropertyPath(ToggleButton.IsCheckedProperty);

                cm.SetBinding(System.Windows.Controls.ContextMenu.IsOpenProperty, b);

                _toggleButton.ContextMenu = cm;
                _toggleButton.Checked += DropdownButton_Checked;
            }          
        }

        /// <summary>
        /// Handle the ToggleButton Checked event that displays a context menu of TabItemBrowser Headers
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DropdownButton_Checked(object sender, RoutedEventArgs e)
        {
            if (_toggleButton == null) return;

            _toggleButton.ContextMenu.Items.Clear();
            if (TabStripPlacement == Dock.Bottom)
                _toggleButton.ContextMenu.Placement = PlacementMode.Top;
            else
                _toggleButton.ContextMenu.Placement = PlacementMode.Bottom;

            XamlSerializer xaml = new XamlSerializer();

            for (int i = 0; i < Items.Count; i++)
            {
                TabItemBrowser item = this.Items[i] as TabItemBrowser;
                if (item == null)
                    return;

                object header = null;
                object icon = null;

                if (item.Header != null)
                    header = xaml.Deserialize(xaml.Serialize(item.Header)); 

                MenuItem mi = new MenuItem() { Header = header, Icon = icon, Tag = i.ToString() };
                mi.SetResourceReference(MenuItem.StyleProperty, "ContextMenuItemHelp");
                mi.Click += ContextMenuItem_Click;
                
                _toggleButton.ContextMenu.Items.Add(mi);
            }
        }       

        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            if (Items.Count == 0)
                return;

            TabItemBrowser ti = null;

            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
            {
                switch (e.Key)
                {
                    case Key.W:
                        RemoveItem((TabItemBrowser)this.SelectedItem);
                        break;

                    case Key.T:
                        AddTabItem();
                        break;
                }
            }

            switch (e.Key)
            {
                case Key.Home:
                    ti = this.Items[0] as TabItemBrowser;
                    break;

                case Key.End:
                    ti = this.Items[Items.Count - 1] as TabItemBrowser;
                    break;

                case Key.Tab:
                    if (e.KeyboardDevice.Modifiers == ModifierKeys.Control)
                    {
                        int index = SelectedIndex;
                        int direction = 1;
                        if (e.KeyboardDevice.Modifiers == ModifierKeys.Shift)
                            direction = -1;

                        while (true)
                        {
                            index += direction;
                            if (index < 0)
                                index = Items.Count - 1;
                            else if (index > Items.Count - 1)
                                index = 0;

                            FrameworkElement ui = Items[index] as FrameworkElement;
                            if (ui.Visibility == Visibility.Visible && ui.IsEnabled)
                            {
                                ti = Items[index] as TabItemBrowser;
                                break;
                            }
                        }
                    }
                    break;
            }

            VirtualizingTabPanel panel = (VirtualizingTabPanel)ControlHelper.FindChild(this, typeof(VirtualizingTabPanel));
            if (panel != null && ti != null)
            {
                panel.MakeVisible(ti, Rect.Empty);
                SelectedItem = ti;

                e.Handled = ti.Focus();
            }
            if (!e.Handled)
                base.OnPreviewKeyDown(e);
        }

        /// <summary>
        /// Handle the MenuItem's Click event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ContextMenuItem_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
            if (mi == null) return;

            int index;
            // get the index of the TabItemBrowser from the manuitems Tag property
            bool b = int.TryParse(mi.Tag.ToString(), out index);

            if (b)
            {
                TabItemBrowser tabItem = this.Items[index] as TabItemBrowser;
                if (tabItem != null)
                {
                    VirtualizingTabPanel itemsHost = (VirtualizingTabPanel)ControlHelper.FindChild(this, typeof(VirtualizingTabPanel));
                    if (itemsHost != null)
                        itemsHost.MakeVisible(tabItem, Rect.Empty);

                    tabItem.Focus();
                }
            }
        }

        /// <summary>
        /// Called by a child TabItemBrowser that wants to remove itself by clicking on the close button
        /// </summary>
        /// <param name="item"></param>
        internal void RemoveItem(TabItemBrowser item)
        {
            // gives an opertunity to cancel the removal of the tabitem
            CancelEventArgs c = new CancelEventArgs();
            if (TabItemBrowserClosing != null)
                TabItemBrowserClosing(item, c);

            if (c.Cancel == true)
                return;

            this.Items.Remove(item);

            if (TabItemBrowserClosed != null)
                TabItemBrowserClosed(this, new RoutedEventArgs());
        }

        public void AddTabItem()
        {
            int i = this.SelectedIndex;

            TabItemBrowser item = new TabItemBrowser();
            item.Header = FindResource("Infra.Controls.Label.NovaAba").ToString();

            if (i == -1 || i == this.Items.Count - 1 || AddNewTabToEnd)
                this.Items.Add(item);
            else
                this.Items.Insert(++i, item);

            if (SelectNewTabOnCreate)
            {
                SelectedItem = item;

                VirtualizingTabPanel itemsHost = (VirtualizingTabPanel)ControlHelper.FindChild(this, typeof(VirtualizingTabPanel));
                if (itemsHost != null)
                    itemsHost.MakeVisible(item, Rect.Empty);

                item.Focus();
            }

            if (TabItemBrowserAdded != null)
                TabItemBrowserAdded(this, new RoutedEventArgs());
        }

        private void createTabItemClick(object sender, ExecutedRoutedEventArgs e)
        {
            AddTabItem();
        }

        private void repeatLeftClick(object sender, ExecutedRoutedEventArgs e)
        {
            ScrollViewer scrollViewer = this.Template.FindName("PART_ScrollViewer", this) as ScrollViewer;

            if (scrollViewer != null)
                scrollViewer.LineLeft();

        }

        private void repeatRightClick(object sender, ExecutedRoutedEventArgs e)
        {
            ScrollViewer scrollViewer = this.Template.FindName("PART_ScrollViewer", this) as ScrollViewer;

            if (scrollViewer != null)
                scrollViewer.LineRight();
        }

        #endregion
    }
}
